﻿using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace Quickuse.Ioc
{
    /// <summary>
    /// 类型查找器
    /// </summary>
    public class TypeFinder
    {
        private readonly IConfiguration _configuration;

        /// <summary>
        /// 程序集集合
        /// </summary>
        public readonly List<Assembly> Assemblies = new List<Assembly>();

        /// <summary>
        /// 
        /// </summary>
        public TypeFinder()
        {
            this.InitAssemblies();
        }

        /// <summary>
        /// 
        /// </summary>
        public TypeFinder(IConfiguration configuration)
        {
            _configuration = configuration;
            this.InitAssemblies();
        }

        /// <summary>
        /// 获得程序集
        /// <remarks>
        /// 根据配置的部分程序集名
        /// </remarks>
        /// </summary>
        private void InitAssemblies()
        {
            string iocAssembliesName = _configuration?["ioc:assemblies"] ?? "";
            string unIocAssembliesName = _configuration?["unioc:assemblies"] ?? "";

            Assembly[] assemblyArray = AppDomain.CurrentDomain.GetAssemblies();

            //查找全局程序集
            var assemblies = CurrentPathAssemblies();

            if (iocAssembliesName.Length == 0)
            {
                var unIocAssembliesNameArray = unIocAssembliesName.ToArrayValue();
                foreach (var assembly in assemblies)
                {
                    if (unIocAssembliesNameArray.Any(e => assembly.FullName.Contains(e)))
                    {
                        continue;
                    }

                    this.Assemblies.Add(assembly);
                }
            }
            else
            {
                //查找配置程序集
                string[] iocAssembliesNameArray = iocAssembliesName.ToArrayValue();
                var unIocAssembliesNameArray = unIocAssembliesName.ToArrayValue();

                foreach (var assembly in assemblies)
                {
                    if (unIocAssembliesNameArray.Any(e => assembly.FullName.Contains(e)))
                    {
                        continue;
                    }

                    if (iocAssembliesNameArray.Any(p => assembly.FullName.Contains(p)))
                    {
                        this.Assemblies.Add(assembly);
                    }
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private IEnumerable<Assembly> CurrentPathAssemblies()
        {
            List<Assembly> assemblies = new List<Assembly>();
            
            string[] filepaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory);
            //TODO:待优化 获取当前项目下所有可以动态加载的程序集
            foreach (var filepath in filepaths)
            {
                //TODO:判断资源是否可以被动态加载
                if (Path.GetExtension(filepath).ToLower() == ".dll")
                {
                    try
                    {
                        assemblies.Add(Assembly.LoadFrom(filepath));
                    }
                    catch (Exception ex)
                    {
                        IocGlobal.Log.Error($"[[TypeFinder.CurrentPathAssemblies=>{ex.Message}{ex.StackTrace}]]");
                    }
                }
            }

            return assemblies;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public virtual IEnumerable<Type> FindClassesOfType<T>()
        {
            List<Type> list = new List<Type>();
            Type typeFromHandle = typeof(T);
            if (this.Assemblies != null && this.Assemblies.Count > 0)
            {
                foreach (Assembly item in this.Assemblies)
                {
                    Type[] types = item.GetTypes();
                    if (types.Length > 0)
                    {
                        Type[] array = types;
                        for (int i = 0; i < array.Length; i++)
                        {
                            Type type = array[i];
                            if (typeFromHandle.IsAssignableFrom(type) || (typeFromHandle.IsGenericTypeDefinition && this.DoesTypeImplementOpenGeneric(type, typeFromHandle)))
                            {
                                if (!type.IsInterface)
                                {
                                    if (type.IsClass && !type.IsAbstract)
                                    {
                                        list.Add(type);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return list;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <param name="openGeneric"></param>
        /// <returns></returns>
        protected virtual bool DoesTypeImplementOpenGeneric(Type type, Type openGeneric)
        {
            try
            {
                Type genericTypeDefinition = openGeneric.GetGenericTypeDefinition();
                Type[] array = type.FindInterfaces((Type objType, object objCriteria) => true, null);
                for (int i = 0; i < array.Length; i++)
                {
                    Type type2 = array[i];
                    if (type2.IsGenericType)
                    {
                        bool flag = genericTypeDefinition.IsAssignableFrom(type2.GetGenericTypeDefinition());
                        var result = flag;
                        return result;
                    }
                }
            }
            catch (Exception ex)
            {
                IocGlobal.Log.Error($"[[TypeFinder.DoesTypeImplementOpenGeneric=>{ex.Message}{ex.StackTrace}]]");
            }
            return false;
        }
    }
}
